﻿using Platformer_Example.Controller.Character;
using Platformer_Example.Controller.Terrain;
using System;
using System.Linq;
using Transformable_Engine_v2.Engine.Helper;
using Transformable_Engine_v2.Engine.Transformable_Math.Geometry;

namespace Platformer_Example.Controller.Collision
{
    public static class CollisionDetection
    {
        public static bool IsBetweenX(TE_Rectangle character, TE_Rectangle terrain)
        {
            return character.Left.IsBetweenExclusive(terrain.Left, terrain.Right) ||
                   character.Right.IsBetweenExclusive(terrain.Left, terrain.Right) ||
                   terrain.Left.IsBetweenExclusive(character.Left, character.Right) ||
                   terrain.Right.IsBetweenExclusive(character.Left, character.Right);
        }

        public static bool IsBetweenY(TE_Rectangle character, TE_Rectangle terrain)
        {
            return character.Top.IsBetween(terrain.Top, terrain.Bottom) ||
                   character.Bottom.IsBetween(terrain.Top, terrain.Bottom) ||
                   terrain.Top.IsBetween(character.Top, character.Bottom) ||
                   terrain.Bottom.IsBetween(character.Top, character.Bottom);
        }

         public static BaseTerrain.TerrainType ResolveCollisionY(Map.Map map, BaseGravityAffected character, float movementOffset)
         {
            BaseTerrain.TerrainType collisionType;
            if (movementOffset > 0)
            {
                //Check for floors
                collisionType = BaseTerrain.TerrainType.Floor;
            }
            else if (movementOffset < 0)
            {
                //Check for ceilings
                collisionType = BaseTerrain.TerrainType.Ceiling;
            }
            else
            {
                return BaseTerrain.TerrainType.None;
            }

             return ResolveCollision(map, character, movementOffset, collisionType);
         }

         public static BaseTerrain.TerrainType ResolveCollisionX(Map.Map map, BaseGravityAffected character, float movementOffset)
         {
             BaseTerrain.TerrainType collisionType;
             if (movementOffset > 0)
             {
                 //Check for leftwalls
                 collisionType = BaseTerrain.TerrainType.Left;
             }
             else if (movementOffset < 0)
             {
                 //Check for rightwalls
                 collisionType = BaseTerrain.TerrainType.Right;
             }
             else
             {
                 return BaseTerrain.TerrainType.None;
             }

             return ResolveCollision(map, character, movementOffset, collisionType);
         }

        static BaseTerrain.TerrainType ResolveCollision(Map.Map map, BaseGravityAffected character, float movementOffset, BaseTerrain.TerrainType collisionType)
        {
            //Get the nearest floor to the player in the direction of the collision
            var options = map.GetTerrain().Where(x => x.IsTerrainValid(collisionType))
                                        .Where(x => x.IsTerrainCorrectlyLocated(collisionType, character))
                                        .Where(x => x.IsTerrainCollidingInOtherAxis(collisionType, character))
                                        .Select(x => new {
                                                            Terrain = x,
                                                            Distance = x.GetDistanceFromCharacter(collisionType, character),
                                                         })
                                        .OrderBy(x => x.Distance)
                                        .ToList();

            var closest = options.FirstOrDefault();

            if (closest != null)
            {
                if (closest.Distance <= Math.Abs(movementOffset))
                {
                    //There is a collision
                    return closest.Terrain.ResolveCollision(character, collisionType, movementOffset);
                }
            }
            return BaseTerrain.TerrainType.None;
        }
    }
}